﻿using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using System.Collections.Generic;
namespace eTMS.Commons
{
    // Read an entire standard DBF file into a DataTable
    public class DbfIO
    {
        // This is the file header for a DBF. We do this special layout with everything
        // packed so we can read straight from disk into the structure to populate it
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct DBFHeader
        {
            public byte version;
            public byte updateYear;
            public byte updateMonth;
            public byte updateDay;
            public Int32 numRecords;
            public Int16 headerLen;
            public Int16 recordLen;
            public Int16 reserved1;
            public byte incompleteTrans;
            public byte encryptionFlag;
            public Int32 reserved2;
            public Int64 reserved3;
            public byte MDX;
            public byte language;
            public Int16 reserved4;
        }

        // This is the field descriptor structure. There will be one of these for each column in the table.
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct FieldDescriptor
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
            public string fieldName;
            public char fieldType;
            public Int32 address;
            public byte fieldLen;
            public byte count;
            public Int16 reserved1;
            public byte workArea;
            public Int16 reserved2;
            public byte flag;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
            public byte[] reserved3;
            public byte indexFlag;
        }
        public static int NumOfDeletedRecord = 0;
        // Read an entire standard DBF file into a DataTable
        public static DataTable ReadDBF(string dbfFile)
        {
            return ReadBlockDBF(dbfFile, 0, -1);
        }
        public static DBFHeader ReadHeader(BinaryReader br)
        {
            if (br == null) return new DBFHeader();
            br.BaseStream.Seek(0, SeekOrigin.Begin);
            byte[] buffer = br.ReadBytes(Marshal.SizeOf(typeof(DBFHeader)));
            // Marshall the header into a DBFHeader structure
            GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            DBFHeader header = (DBFHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(DBFHeader));
            handle.Free();
            return header;
        }
        public static DBFHeader ReadHeader(string FileName)
        {
            DBFHeader header = new DBFHeader();
            try
            {
                System.IO.BinaryReader br = new BinaryReader(File.OpenRead(FileName), Encoding.Unicode);
                try
                {
                    header = ReadHeader(br);
                }
                catch
                {
                }
                br.Close();
            }
            catch
            {
            }
            return header;
        }
        public static ArrayList ReadFields(BinaryReader br, DBFHeader header)
        {
            br.BaseStream.Seek(Marshal.SizeOf(typeof(DBFHeader)), SeekOrigin.Begin);
            ArrayList fields = new ArrayList();
            while ((13 != br.PeekChar()))
            {
                byte[] buffer = br.ReadBytes(Marshal.SizeOf(typeof(FieldDescriptor)));
                GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                fields.Add((FieldDescriptor)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(FieldDescriptor)));
                handle.Free();
                if (br.BaseStream.Position > header.headerLen - 2) break;
            }
            return fields;
        }
        public static DataTable ReadSchema(string dbfFile)
        {
            DataTable dt = new DataTable();
            // If there isn't even a file, just return an empty DataTable
            if ((false == File.Exists(dbfFile)))
            {
                return dt;
            }

            BinaryReader br = null;
            try
            {
            // Read the header into a buffer

                br = new BinaryReader(File.OpenRead(dbfFile), Encoding.Unicode);
            //try
            //{
                DBFHeader header = ReadHeader(br);
                // Read in all the field descriptors. Per the spec, 13 (0D) marks the end of the field descriptors
                ArrayList fields = ReadFields(br, header);
                dt = ReadSchema(br, header, fields);
            }
            catch
            {

            }
            finally
            {
                if (null != br)
                {
                    br.Close();
                }
            }

            return dt;
        }

        public static DataTable ReadSchema(BinaryReader br, DBFHeader header, ArrayList fields)
        {
            DataTable dt = new DataTable();
            BinaryReader recReader;
            string number;

            try
            {
                byte[] buffer;
                // Read in the first row of records, we need this to help determine column types below
                ((FileStream)br.BaseStream).Seek(header.headerLen + 1, SeekOrigin.Begin);
                buffer = br.ReadBytes(header.recordLen);
                recReader = new BinaryReader(new MemoryStream(buffer));
                // Create the columns in our new DataTable
                DataColumn col = null;
                foreach (FieldDescriptor field in fields)
                {
                    number = Encoding.ASCII.GetString(recReader.ReadBytes(field.fieldLen));
                    switch (field.fieldType)
                    {
                        case 'N':
                            if (number.IndexOf(".") > -1)
                            {
                                col = new DataColumn(field.fieldName, typeof(decimal));
                            }
                            else
                            {
                                col = new DataColumn(field.fieldName, typeof(int));
                            }
                            break;
                        case 'C':
                            col = new DataColumn(field.fieldName, typeof(string));
                            col.MaxLength = field.fieldLen;
                            break;
                        case 'D':
                            col = new DataColumn(field.fieldName, typeof(DateTime));
                            break;
                        case 'L':
                            col = new DataColumn(field.fieldName, typeof(bool));
                            break;
                        case 'F':
                            col = new DataColumn(field.fieldName, typeof(Double));
                            break;
                        case 'M':
                            col = new DataColumn(field.fieldName);
                            break;
                        default:
                            col = new DataColumn(field.fieldName);
                            break;
                    }
                    dt.Columns.Add(col);
                }

            }
            catch
            {

            }
            finally
            {
                if (null != br)
                {
                    br.Close();
                }
            }

            return dt;
        }

        public static ConverterFont.FontType FontType = ConverterFont.FontType.VRE;

        public static void ReadBlockDBF(DBFHeader header, ArrayList fields, DataTable dt, BinaryReader br, int startRecord, int recordCount)
        {
            NumOfDeletedRecord = 0;
            // Skip past the end of the header. 
            if (startRecord < 0) startRecord = 0;
            if (startRecord * header.recordLen + header.headerLen < br.BaseStream.Length)
            {
                ((FileStream)br.BaseStream).Seek(header.headerLen + startRecord * header.recordLen, SeekOrigin.Begin);
                int countRecord = 0;
                // Read in all the records
                for (int counter = 0; counter <= header.numRecords - 1; counter++)
                {

                    if (countRecord == recordCount)
                    {
                        break;
                    }
                    if (br.BaseStream.Position >= br.BaseStream.Length - 1) break;
                    // First we'll read the entire record into a buffer and then read each field from the buffer
                    // This helps account for any extra space at the end of each record and probably performs better
                    byte[] buffer = br.ReadBytes(header.recordLen);
                    BinaryReader recReader = new BinaryReader(new MemoryStream(buffer));

                    // All dbf field records begin with a deleted flag field. Deleted - 0x2A (asterisk) else 0x20 (space)
                    char chr = ' ';
                    if (recReader.PeekChar() > 0)
                        chr = recReader.ReadChar();
                    if (chr == '*')
                    {
                        NumOfDeletedRecord++;
                        continue;
                    }
                    countRecord++;
                    // Loop through each field in a record
                    DataRow row = dt.NewRow();
                    foreach (FieldDescriptor field in fields)
                    {
                        switch (field.fieldType)
                        {
                            case 'N':  // Number
                                // If you port this to .NET 2.0, use the Decimal.TryParse method
                                buffer = recReader.ReadBytes(field.fieldLen);
                                string number = Encoding.ASCII.GetString(buffer);
                              if(number.Trim()=="20534")
                              {
                                  string st="";
                              }
                                if (IsNumber(number))
                                {
                                    if (number.IndexOf(".") > -1)
                                    {
                                        try
                                        {
                                            row[field.fieldName] = decimal.Parse(number);
                                        }
                                        catch
                                        {
                                        }
                                    }
                                    else
                                    {
                                        try
                                        {
                                            row[field.fieldName] = int.Parse(number);
                                        }
                                        catch
                                        {
                                        }
                                    }
                                }
                                else
                                {
                                    row[field.fieldName] = 0;
                                }

                                break;

                            case 'C': // String
                                string content = Encoding.Default.GetString(recReader.ReadBytes(field.fieldLen));
                                if (FontType == ConverterFont.FontType.VRE)
                                {
                                    row[field.fieldName] = ConverterFont.VREToUnicode(content);
                                }
                                else if (FontType == ConverterFont.FontType.TCVN3)
                                {
                                    row[field.fieldName] = ConverterFont.TCVN3ToUnicode(content);
                                }
                                break;

                            case 'D': // Date (YYYYMMDD)
                                string year = Encoding.ASCII.GetString(recReader.ReadBytes(4));
                                string month = Encoding.ASCII.GetString(recReader.ReadBytes(2));
                                string day = Encoding.ASCII.GetString(recReader.ReadBytes(2));
                                row[field.fieldName] = System.DBNull.Value;
                                try
                                {
                                    if (IsNumber(year) && IsNumber(month) && IsNumber(day))
                                    {
                                        if ((Int32.Parse(year) > 1900))
                                        {
                                            row[field.fieldName] = new DateTime(Int32.Parse(year), Int32.Parse(month), Int32.Parse(day));
                                        }
                                    }
                                }
                                catch
                                { }

                                break;

                            case 'L': // Boolean (Y/N)
                                char bChar=(char)recReader.ReadByte();
                                row[field.fieldName] = true;
                                if (('Y' == bChar)||('T' == bChar))
                                {
                                    row[field.fieldName] = true;
                                }                                
                                if(('N' == bChar)||('F' == bChar))
                                {
                                    row[field.fieldName] = false;
                                }

                                break;

                            case 'F':
                                number = Encoding.ASCII.GetString(recReader.ReadBytes(field.fieldLen));
                                if (IsNumber(number))
                                {
                                    try
                                    {
                                        row[field.fieldName] = double.Parse(number);
                                    }
                                    catch
                                    {
                                    }
                                }
                                break;
                            case 'M':
                                byte[] buff = recReader.ReadBytes(field.fieldLen);
                                number = Encoding.ASCII.GetString(buff);
                                row[field.fieldName] = number;
                                break;
                            default:
                                row[field.fieldName] = recReader.ReadBytes(field.fieldLen);
                                break;
                        }
                    }

                    recReader.Close();
                    dt.Rows.Add(row);
                }
            }
        }

        public static DataTable ReadBlockDBF(string dbfFile, int startRecord, int recordCount)
        {
            DataTable dt = new DataTable();
            BinaryReader recReader;
            string number;
            string year;
            string month;
            string day;
            DataRow row;

            // If there isn't even a file, just return an empty DataTable
            if ((false == File.Exists(dbfFile)))
            {
                return dt;
            }

            BinaryReader br = new BinaryReader(System.IO.File.Open(dbfFile, FileMode.Open), Encoding.Unicode);
            // try
            //{
            // Read the header into a buffer
            DBFHeader header = ReadHeader(br);
            // Read in all the field descriptors. Per the spec, 13 (0D) marks the end of the field descriptors


            ArrayList fields = ReadFields(br, header);


            // Read in the first row of records, we need this to help determine column types below
            ((FileStream)br.BaseStream).Seek(header.headerLen + 1, SeekOrigin.Begin);
            byte[] buffer = br.ReadBytes(header.recordLen);
            recReader = new BinaryReader(new MemoryStream(buffer));

            // Create the columns in our new DataTable
            DataColumn col = null;

            foreach (FieldDescriptor field in fields)
            {
                number = Encoding.ASCII.GetString(recReader.ReadBytes(field.fieldLen));
                switch (field.fieldType)
                {
                    case 'N':
                        if (number.IndexOf(".") > -1)
                        {
                            col = new DataColumn(field.fieldName, typeof(decimal));
                        }
                        else
                        {
                            col = new DataColumn(field.fieldName, typeof(int));
                        }
                        break;
                    case 'C':
                        col = new DataColumn(field.fieldName, typeof(string));
                        break;
                    case 'D':
                        col = new DataColumn(field.fieldName, typeof(DateTime));
                        break;
                    case 'L':
                        col = new DataColumn(field.fieldName, typeof(bool));
                        break;
                    case 'F':
                        col = new DataColumn(field.fieldName, typeof(Double));
                        break;
                    case 'M':
                        col = new DataColumn(field.fieldName);
                        break;
                    default:
                        col = new DataColumn(field.fieldName);
                        break;
                }
                dt.Columns.Add(col);
            }
            ReadBlockDBF(header, fields, dt, br, startRecord, recordCount);
            br.Close();
            return dt;
        }



        public static DataTable ReadDBF(string dbFile, string Filter)
        {
            int BlockNum = 10000;
            DataTable tblData = Commons.DbfIO.ReadSchema(dbFile);
            System.IO.BinaryReader br = new System.IO.BinaryReader(System.IO.File.OpenRead(dbFile));
            Commons.DbfIO.DBFHeader header = Commons.DbfIO.ReadHeader(br);
            ArrayList fields = Commons.DbfIO.ReadFields(br, header);
            DataTable tblDataTemp = tblData.Clone();
            int index = 0;
            while (true)
            {
                Commons.DbfIO.ReadBlockDBF(header, fields, tblDataTemp, br, index, BlockNum);
                if (tblDataTemp.Rows.Count > 0)
                {
                    tblDataTemp.DefaultView.RowFilter = Filter;
                    foreach (DataRowView r in tblDataTemp.DefaultView)
                    {
                        tblData.ImportRow(r.Row);
                    }
                    index += tblDataTemp.Rows.Count;
                    if (tblDataTemp.Rows.Count < BlockNum)
                    {
                        break;
                    }
                    tblDataTemp.Dispose();
                    tblDataTemp = tblData.Clone();
                }
                else
                {
                    break;
                }
            }
            br.Close();
            return tblData;
        }

        /// <summary>
        /// Simple function to test is a string can be parsed. There may be a better way, but this works
        /// If you port this to .NET 2.0, use the new TryParse methods instead of this
        /// </summary>
        /// <param name="number">string to test for parsing</param>
        /// <returns>true if string can be parsed</returns>
        public static bool IsNumber(string numberString)
        {
            char[] numbers = numberString.Trim().ToCharArray();
            int start = 0;
            if (numbers.Length == 0) return false;
            if (numbers[0] == '-')
                start = 1;

            for (int i = start; i < numbers.Length; i++)
            {
                char number = numbers[i];
                if ((number < 48 || number > 57) && number != 46 && number != 32)
                {
                    return false;
                }
            }

            return true;
        }
        private static string FixLen(string st, int len)
        {
            string stfix = "                                                      ";
            if (st.Length < len)
                st = stfix.Substring(stfix.Length + st.Length - len) + st;
            return st;
        }

        public static void WriteDBF(string dbfFile, DataTable tblData)
        {
            BinaryReader recReader;
            string number;
            string year;
            string month;
            string day;
            DataRow row;

            // If there isn't even a file, just return an empty DataTable


            BinaryWriter bw = null;
            try
            {
                // Read the header into a buffer
                bw = new BinaryWriter(File.Create(dbfFile));

                // Marshall the header into a DBFHeader structure
                GCHandle handle;
                IntPtr ptr;
                DBFHeader header = CreateHeader(tblData);
                List<FieldDescriptor> arrFields = CreateFieldsDescription(tblData);
                MemoryStream strmem = new MemoryStream();
                byte[] buffer;
                foreach (FieldDescriptor field in arrFields)
                {
                    buffer = new byte[Marshal.SizeOf(typeof(FieldDescriptor))];
                    handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    Marshal.StructureToPtr(field, handle.AddrOfPinnedObject(), true);
                    strmem.Write(buffer, 0, buffer.Length);
                    handle.Free();
                }
                byte[] tmp = strmem.ToArray();
                strmem.Close();
                strmem = new MemoryStream();
                buffer = new byte[Marshal.SizeOf(typeof(DBFHeader))];
                header.headerLen = (short)(buffer.Length + tmp.Length + 1);
                FieldDescriptor lastField = ((FieldDescriptor)arrFields[arrFields.Count - 1]);
                header.recordLen = (short)(lastField.address + lastField.fieldLen + 1);
                handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                Marshal.StructureToPtr(header, handle.AddrOfPinnedObject(), true);
                strmem.Write(buffer, 0, buffer.Length);
                handle.Free();
                bw.Write(strmem.ToArray());
                bw.Write(tmp);
                bw.Write((byte)13);
                strmem.Close();
                foreach (DataRow r in tblData.Rows)
                {
                    bw.Write((byte)32);
                    int index = 0;
                    foreach (FieldDescriptor field in arrFields)
                    {
                        switch (field.fieldType)
                        {
                            case 'N':
                                if (field.count > 0)
                                {
                                    if (!r.IsNull(index))
                                    {
                                        buffer = System.Text.Encoding.ASCII.GetBytes(FixLen(Math.Round(Convert.ToDecimal(r[index]), field.count).ToString(), field.fieldLen));
                                    }
                                    else
                                    {
                                        buffer = new byte[0];
                                    }
                                    if (buffer.Length < field.fieldLen)
                                    {
                                        tmp = new byte[field.fieldLen];
                                        if (buffer.Length > 0)
                                            Array.Copy(buffer, tmp, buffer.Length);
                                        buffer = tmp;
                                    }
                                }
                                else
                                {
                                    if (!r.IsNull(index))
                                    {
                                        buffer = System.Text.Encoding.ASCII.GetBytes(FixLen(Convert.ToInt32(r[index]).ToString(), field.fieldLen));
                                    }
                                    else
                                    {
                                        buffer = new byte[0];
                                    }
                                    if (buffer.Length < field.fieldLen)
                                    {
                                        tmp = new byte[field.fieldLen];
                                        if (buffer.Length > 0)
                                            Array.Copy(buffer, tmp, buffer.Length);
                                        buffer = tmp;
                                    }
                                }
                                bw.Write(buffer, 0, Math.Min(buffer.Length, field.fieldLen));
                                break;
                            case 'F':
                                if (!r.IsNull(index))
                                {
                                    buffer = System.Text.Encoding.ASCII.GetBytes(FixLen(Math.Round(Convert.ToDouble(r[index]), field.count).ToString(), field.fieldLen));
                                }
                                else
                                {
                                    buffer = new byte[0];
                                }
                                if (buffer.Length < field.fieldLen)
                                {
                                    tmp = new byte[field.fieldLen];
                                    if (buffer.Length > 0)
                                        Array.Copy(buffer, tmp, buffer.Length);
                                    buffer = tmp;
                                }
                                bw.Write(buffer, 0, Math.Min(buffer.Length, field.fieldLen));
                                break;
                            case 'L':
                                bool value = false;
                                try
                                {
                                    value = (bool)r[index];
                                }
                                catch
                                {
                                }
                                string strval = value ? "Y" : "N";
                                buffer = System.Text.Encoding.ASCII.GetBytes(strval);
                                bw.Write(buffer);
                                break;
                            case 'C':
                                string content = "";
                                if (!r.IsNull(index)) content = r[index].ToString();
                                if (FontType == ConverterFont.FontType.VRE)
                                {
                                    content = ConverterFont.UnicodeToVRE(content);
                                }
                                else if (FontType == ConverterFont.FontType.TCVN3)
                                {
                                    content = ConverterFont.UnicodeToTCVN3(content);
                                }
                                buffer = System.Text.Encoding.Default.GetBytes(content);
                                if (buffer.Length < field.fieldLen)
                                {
                                    tmp = new byte[field.fieldLen];
                                    Array.Copy(buffer, tmp, buffer.Length);
                                    for (int idx = buffer.Length; idx < field.fieldLen; idx++)
                                    {
                                        tmp[idx] = 32;
                                    }
                                    buffer = tmp;

                                }
                                bw.Write(buffer, 0, Math.Min(buffer.Length, field.fieldLen));
                                break;
                            case 'D':
                                DateTime dt;
                                try
                                {
                                    dt = (DateTime)r[index];
                                    buffer = System.Text.Encoding.Default.GetBytes(dt.Year.ToString("0000"));
                                    bw.Write(buffer);
                                    buffer = System.Text.Encoding.Default.GetBytes(dt.Month.ToString("00"));
                                    bw.Write(buffer);
                                    buffer = System.Text.Encoding.Default.GetBytes(dt.Day.ToString("00"));
                                    bw.Write(buffer);

                                }
                                catch
                                {
                                    dt = new DateTime();
                                    buffer = System.Text.Encoding.Default.GetBytes(dt.Year.ToString("0000"));
                                    bw.Write(buffer);
                                    buffer = System.Text.Encoding.Default.GetBytes(dt.Month.ToString("00"));
                                    bw.Write(buffer);
                                    buffer = System.Text.Encoding.Default.GetBytes(dt.Day.ToString("00"));
                                    bw.Write(buffer);

                                }
                                break;
                        }
                        index++;
                    }
                }
            }

            catch
            {
                throw;
            }
            finally
            {
                if (null != bw)
                {
                    bw.Close();
                }
            }

        }
        public static void WriteDBFHeader(BinaryWriter bw, DBFHeader header, List<FieldDescriptor> arrFields)
        {
            MemoryStream strmem = new MemoryStream();
            bw.BaseStream.Position = 0;
            byte[] buffer;
            GCHandle handle;
            foreach (FieldDescriptor field in arrFields)
            {
                buffer = new byte[Marshal.SizeOf(typeof(FieldDescriptor))];
                handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                Marshal.StructureToPtr(field, handle.AddrOfPinnedObject(), true);
                strmem.Write(buffer, 0, buffer.Length);
                handle.Free();
            }
            byte[] tmp = strmem.ToArray();
            strmem.Close();
            strmem = new MemoryStream();
            buffer = new byte[Marshal.SizeOf(typeof(DBFHeader))];
            header.headerLen = (short)(buffer.Length + tmp.Length + 1);
            FieldDescriptor lastField = ((FieldDescriptor)arrFields[arrFields.Count - 1]);
            header.recordLen = (short)(lastField.address + lastField.fieldLen + 1);
            handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            Marshal.StructureToPtr(header, handle.AddrOfPinnedObject(), true);
            strmem.Write(buffer, 0, buffer.Length);
            handle.Free();
            bw.Write(strmem.ToArray());
            bw.Write(tmp);
            bw.Write((byte)13);
            strmem.Close();
        }
        public static void WriteDBFAppend(BinaryWriter bw, DataTable tblData,ref DBFHeader header, List<FieldDescriptor> arrFields)
        {
            byte[] buffer;
            byte[] tmp;
            foreach (DataRow r in tblData.Rows)
            {
                bw.Write((byte)32);
                int index = 0;
                foreach (FieldDescriptor field in arrFields)
                {
                    switch (field.fieldType)
                    {
                        case 'N':
                            if (field.count > 0)
                            {
                                if (!r.IsNull(index))
                                {
                                    buffer = System.Text.Encoding.ASCII.GetBytes(FixLen(Math.Round(Convert.ToDecimal(r[index]), field.count).ToString(), field.fieldLen));
                                }
                                else
                                {
                                    buffer = new byte[0];
                                }
                                if (buffer.Length < field.fieldLen)
                                {
                                    tmp = new byte[field.fieldLen];
                                    if (buffer.Length > 0)
                                        Array.Copy(buffer, tmp, buffer.Length);
                                    buffer = tmp;
                                }
                            }
                            else
                            {
                                if (!r.IsNull(index))
                                {
                                    buffer = System.Text.Encoding.ASCII.GetBytes(FixLen(Convert.ToInt32(r[index]).ToString(), field.fieldLen));
                                }
                                else
                                {
                                    buffer = new byte[0];
                                }
                                if (buffer.Length < field.fieldLen)
                                {
                                    tmp = new byte[field.fieldLen];
                                    if (buffer.Length > 0)
                                        Array.Copy(buffer, tmp, buffer.Length);
                                    buffer = tmp;
                                }
                            }
                            bw.Write(buffer, 0, Math.Min(buffer.Length, field.fieldLen));
                            break;
                        case 'F':
                            if (!r.IsNull(index))
                            {

                                buffer = System.Text.Encoding.ASCII.GetBytes(FixLen(Math.Round(Convert.ToDouble(r[index]), field.count).ToString(), field.fieldLen));

                            }
                            else
                            {
                                buffer = new byte[0];
                            }
                            if (buffer.Length < field.fieldLen)
                            {
                                tmp = new byte[field.fieldLen];
                                if (buffer.Length > 0)
                                    Array.Copy(buffer, tmp, buffer.Length);
                                buffer = tmp;
                            }
                            bw.Write(buffer, 0, Math.Min(buffer.Length, field.fieldLen));
                            break;
                        case 'L':
                            bool value = false;
                            try
                            {
                                value = (bool)r[index];
                            }
                            catch
                            {
                            }
                            string strval = value ? "Y" : "N";
                            buffer = System.Text.Encoding.ASCII.GetBytes(strval);
                            bw.Write(buffer);
                            break;
                        case 'C':
                            string content = "";
                            if (!r.IsNull(index)) content = r[index].ToString();
                            if (FontType == ConverterFont.FontType.VRE)
                            {
                                content = ConverterFont.UnicodeToVRE(content);
                            }
                            else if (FontType == ConverterFont.FontType.TCVN3)
                            {
                                content = ConverterFont.UnicodeToTCVN3(content);
                            }
                            else
                                content = ConverterFont.UnicodeToVRE(content);

                            buffer = System.Text.Encoding.Default.GetBytes(content);
                            if (buffer.Length < field.fieldLen)
                            {
                                tmp = new byte[field.fieldLen];
                                Array.Copy(buffer, tmp, buffer.Length);
                                for (int idx = buffer.Length; idx < field.fieldLen; idx++)
                                {
                                    tmp[idx] = 32;
                                }
                                buffer = tmp;

                            }
                            bw.Write(buffer, 0, Math.Min(buffer.Length, field.fieldLen));
                            break;
                        case 'D':
                            DateTime dt;
                            try
                            {
                                dt = (DateTime)r[index];
                                buffer = System.Text.Encoding.Default.GetBytes(dt.Year.ToString("0000"));
                                bw.Write(buffer);
                                buffer = System.Text.Encoding.Default.GetBytes(dt.Month.ToString("00"));
                                bw.Write(buffer);
                                buffer = System.Text.Encoding.Default.GetBytes(dt.Day.ToString("00"));
                                bw.Write(buffer);

                            }
                            catch
                            {
                                dt = new DateTime();
                                buffer = System.Text.Encoding.Default.GetBytes(dt.Year.ToString("0000"));
                                bw.Write(buffer);
                                buffer = System.Text.Encoding.Default.GetBytes(dt.Month.ToString("00"));
                                bw.Write(buffer);
                                buffer = System.Text.Encoding.Default.GetBytes(dt.Day.ToString("00"));
                                bw.Write(buffer);

                            }
                            break;
                    }
                    index++;
                }
            }
            header.numRecords += tblData.Rows.Count;

        }

        public static DBFHeader CreateHeader(DataTable tblData)
        {
            DBFHeader header = new DBFHeader();
            header.version = 3;
            header.updateYear = 7;
            header.updateMonth = 8;
            header.updateDay = 6;
            header.numRecords = tblData.Rows.Count;
            header.language = 3;
            header.headerLen = 0;
            return header;
        }
        public static List<FieldDescriptor> CreateFieldsDescription(DataTable tblData)
        {
            List<FieldDescriptor> arr = new List<FieldDescriptor>();
            int curaddress = 0;
            foreach (DataColumn col in tblData.Columns)
            {
                FieldDescriptor field = new FieldDescriptor();
                field.address = curaddress;
                byte len = 0;
                byte count = 0;
                if (col.DataType.Equals(typeof(int)) || col.DataType.Equals(typeof(long)) || col.DataType.Equals(typeof(byte)) || col.DataType.Equals(typeof(decimal)) || col.DataType.Equals(typeof(double)) || col.DataType.Equals(typeof(Single)) || col.DataType.Equals(typeof(float)))
                {
                    field.fieldType = 'N';
                    switch (col.DataType.FullName)
                    {
                        case "System.Decimal":
                        case "System.Single":
                        case "System.Double":
                            field.fieldLen = (byte)Marshal.SizeOf(typeof(Double));
                            field.count = 0;
                            field.fieldType = 'F';
                            if (tblData.Rows.Count > 0)
                            {
                                len = 0;
                                count = 0;
                                GetFieldLen(tblData, col, ref len, ref count);
                                //field.fieldLen = len;
                                field.count = count;
                            }
                            break;
                        case "System.Int64":
                            field.fieldLen = 19;
                            break;
                        case "System.Int32":
                            field.fieldLen = 10;
                            break;
                        case "System.Int16":
                            field.fieldLen = 5;
                            break;
                        case "System.Byte":
                        case "System.SByte":
                            field.fieldLen = 3;
                            break;
                    }

                }
                else if (col.DataType.Equals(typeof(bool)))
                {
                    field.count = 0;
                    field.fieldLen = 1;
                    field.fieldType = 'L';
                }
                else if (col.DataType.Equals(typeof(DateTime)))
                {
                    field.count = 0;
                    field.fieldLen = 8;
                    field.fieldType = 'D';
                }
                else
                {

                    len = 0;
                    count = 0;
                    if (col.MaxLength < 255)
                        field.fieldLen = (byte)col.MaxLength;
                    else
                        if (tblData.Rows.Count > 0)
                        {
                            GetFieldLen(tblData, col, ref len, ref count);
                            field.fieldLen = len;
                        }
                        else
                        {
                            field.fieldLen = 255;
                        }
                    field.fieldType = 'C';
                }
                field.fieldName = col.ColumnName.Trim().ToUpper();
                field.reserved3 = new byte[] { 0, 0, 0, 0, 0, 0, 0 };
                arr.Add(field);
                curaddress += field.fieldLen;
            }
            return arr;
        }
        private static void GetFieldLen(DataTable tblData, DataColumn col, ref byte Len, ref byte Count)
        {
            Len = 0;
            Count = 0;
            foreach (DataRow r in tblData.Rows)
            {
                string val = r[col].ToString();
                Len = (byte)Math.Max(Len, val.Length);
                int indexdot = val.LastIndexOf(".");
                if (indexdot >= 0)
                    Count = (byte)Math.Max(Count, val.Length - indexdot - 1);
            }
        }
    }
}